use crate::codec::{DecodeRef, Decoder, DecoderExt, Encode, Encoder};
use crate::errors::CodecError;

/// Maximum varint value (2 ** 30).
pub const MAX: usize = 1 << 30 - 1;

/// Minimum varint value (0).
pub const MIN: usize = 0;

/// MLS-style varint, stores up to 2 ** 30.
///
/// See `https://datatracker.ietf.org/doc/rfc942/` § 2.1.2.
pub struct Varint([u8; 4]);

impl Varint {
    /// Converts a u32 to a varint, if it fits.
    pub fn new(v: u32) -> Result<Self, CodecError> {
        let mut buf = [0; 4];
        match v {
            v if v <= 63 => {
                buf[0] = v as u8;
            }
            v if v <= 16383 => {
                let vb = (v as u16).to_be_bytes();
                buf[..2].copy_from_slice(&vb[..]);
                buf[0] |= 0b01 << 6;
            }
            v if v <= 1073741823 => {
                buf = v.to_be_bytes();
                buf[0] |= 0b10 << 6;
            }
            _ => {
                return Err(CodecError::InvalidVarintVal(v as u64));
            }
        }

        Ok(Self(buf))
    }

    /// The length of an encoded buffer.
    pub fn encoded_len(&self) -> usize {
        match self.0[0] >> 6 {
            0 => 1,
            1 => 2,
            2 => 4,
            t => panic!("varint: invalid prefix tag {}", t),
        }
    }

    /// Returns the encoded varint as a slice.
    pub fn as_slice(&self) -> &[u8] {
        let l = self.encoded_len();
        &self.0[..l]
    }

    /// Converts the varint back into a u32.
    pub fn to_u32(&self) -> u32 {
        match self.encoded_len() {
            1 => self.0[0] as u32,
            2 => {
                let mut vb = [0; 2];
                vb.copy_from_slice(&self.0[..2]);
                vb[0] &= 0b00111111;
                u16::from_be_bytes(vb) as u32
            }
            4 => {
                let mut vb = [0; 4];
                vb.copy_from_slice(&self.0);
                vb[0] &= 0b00111111;
                u32::from_be_bytes(vb)
            }
            _ => unreachable!(),
        }
    }
}

impl Encode for Varint {
    fn encode<E: Encoder>(&self, enc: &mut E) -> Result<(), CodecError> {
        enc.push_next(self.as_slice())?;
        Ok(())
    }
}

impl<'de> DecodeRef<'de> for Varint {
    fn decode_ref<'c, D: Decoder<'de>>(dec: &'c mut D) -> Result<Self, CodecError>
    where
        'de: 'c,
    {
        let b = dec.take_u8()?;
        let t = b >> 6 & 0b11;
        let mut buf = [0; 4];
        match t {
            0 => {
                buf[0] = b;
            }
            1 => {
                buf[0] = b;
                buf[1] = dec.take_u8()?;
            }
            2 => {
                buf[0] = b;
                let peek = dec.take_next(3)?;
                buf[1..4].copy_from_slice(peek);
            }
            _ => return Err(CodecError::InvalidVarintTag),
        }
        Ok(Self(buf))
    }
}

impl TryFrom<u64> for Varint {
    type Error = CodecError;

    fn try_from(value: u64) -> Result<Self, Self::Error> {
        if value > MAX as u64 {
            return Err(CodecError::InvalidVarintVal(value));
        }

        Ok(Self::new(value as u32).unwrap())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::util::*;

    #[test]
    fn test_varint_spec_vectors() {
        let vectors: &[(&[u8], u32)] = &[
            (&[0x9d, 0x7f, 0x3e, 0x7d], 494878333),
            (&[0x7b, 0xbd], 15293),
            (&[0x25], 37),
        ];

        for (ev, dv) in vectors {
            let vi: Varint = decode(ev).expect("test: decode varint");
            assert_eq!(vi.to_u32(), *dv);

            let dvvi = Varint::new(*dv).unwrap();
            let dvb = encode_to_vec(&dvvi).expect("test: encode varint");
            assert_eq!(&dvb, ev);
        }
    }
}
